if ($args.Count -lt 3) {
    Write-Host "Error"

    exit -1
}

$allPlatforms = @("win32", "x64")
$allTargets = @("debug", "release")
$allVersions = @("msvc10", "msvc11")

$ignoreTypes = @(".vxproj", ".cmake", ".rule", ".filters", ".vcxproj", ".vcproj", ".svn", ".stamp", ".depend", ".tlog", ".lastbuildstate", ".obj", ".tmp", ".txt", ".htm")
$ignoreNames = @("llvm-*", "llvm_*")

$defaultLogLevel = "normal"

$logLevel = $defaultLogLevel

$llvmPath = $args[0].ToLower()
$config = $args[1].ToLower()
$version = $args[2].ToLower()

if ($args.Count -gt 3) {
    for ($i = 3; $i -lt $args.Count; $i++) {
        $command = $args[$i].ToLower()

        switch ($command) {
            "-log" {
                $parameter = $args[$i + 1].ToLower()

                if ($parameter) {
                    $i++
                }

                switch ($parameter) {
                    "quiet" { $logLevel = "quiet" }
                    "minimal" { $logLevel = "minimal" }
                    "normal" { $logLevel = "normal" }
                    "detailed" { $logLevel = "detailed" }
                    "default" { $logLevel = $defaultLogLevel }
                    default {
                        Write-Host "Error: Invalid log level specified.  Using default setting."
                    }
                }
            }
        }
    }
}

if (!(Test-Path $llvmPath)) {
    Write-Host "Error"

    exit -1
}

if(!$config) {
    Write-Host "Error"

    exit -1
}

if(!$version) {
    Write-Host "Error"

    exit -1
}

$platforms = @()
$targets = @()

if ($config -ne "all") {
    $configSplit = $config.Split(":")

    if ($configSplit.Count -ne 2) {
        Write-Host "Error: Configuration is invalid.  Must be of the form Platform:Release or All"

        exit -1
    } else {
        if ($configSplit[0] -eq "all") {
            $platforms = $allPlatforms
        } else {
            $validPlatform = $FALSE

            for ($i = 0; $i -lt $allPlatforms.Count; $i++) {
                if ($configSplit[0] -eq $allPlatforms[$i]) {
                    $validPlatform = $TRUE

                    break
                }
            }

            if ($validPlatform) {
                $platforms = @($configSplit[0])
            } else {
                Write-Host "Error: Configuration is invalid.  Must be of the form Platform:Release or All"

                exit -1
            }
        }

        if ($configSplit[1] -eq "all") {
            $targets = $allTargets
        } else {
            $validTarget = $FALSE

            for ($i = 0; $i -lt $allTargets.Count; $i++) {
                if ($configSplit[1] -eq $allTargets[$i]) {
                    $validTarget = $TRUE

                    break
                }
            }

            if ($validTarget) {
                $targets = @($configSplit[1])
            } else {
                Write-Host "Error: Configuration is invalid.  Must be of the form Platform:Release or All"

                exit -1
            }
        }
    }
} else {
    $platforms = $allPlatforms
    $targets = $allTargets
}

if ($version -ne "all")  {
    $validVersion = $FALSE

    for ($i = 0; $i -lt $allVersions.Count; $i++) {
        if ($version -eq $allVersions[$i]) {
            $validVersion = $TRUE

            break
        }
    }

    if (!$validVersion) {
        Write-Host "Error: Version is invalid"

        exit -1
    }

    $versions = @($version)
} else {
    $versions = $allVersions
}

$toolPath = Get-Location

for ($i = 0; $i -lt $platforms.Count; $i++) {
    for ($j = 0; $j -lt $targets.Count; $j++) {
        for ($k = 0; $k -lt $versions.Count; $k++) {
            Set-Location $llvmPath

            if (($versions[$k] -eq "msvc10") -or ($versions[$k] -eq "msvc11")) {
                if (!(Test-Path $platforms[$i])) {
                    New-Item -ItemType directory -Path $platforms[$i] | Out-Null
                }

                Set-Location $platforms[$i]

                if (!(Test-Path $versions[$k])) {
                    New-Item -ItemType directory -Path $versions[$k] | Out-Null
                }

                Set-Location $versions[$k]

                $workingPath = Get-Location

                # generate solution

                $makeExpression = "cmake -G"
				if ($versions[$k] -eq "msvc10") {
					if ($platforms[$i] -eq "win32") {
						$makeExpression += ' "Visual Studio 10"'
					} else {
						$makeExpression += ' "Visual Studio 10 Win64"'
					}
				} else {
					if ($platforms[$i] -eq "win32") {
						$makeExpression += ' "Visual Studio 11"'
					} else {
						$makeExpression += ' "Visual Studio 11 Win64"'
					}				
				}

                $makeExpression += " -DLLVM_TARGETS_TO_BUILD=X86 -DBUILD_SHARED_LIBS=FALSE -DLLVM_INCLUDE_EXAMPLES=FALSE -DLLVM_INCLUDE_TESTS=FALSE -DLLVM_INCLUDE_TOOLS=FALSE ../.."

                try {
                    Write-Host ""
                    Write-Host "Generating Solution"
                    Write-Host ""

                    if ($logLevel -ne "quiet") {
                        Invoke-Expression -Command $makeExpression -ErrorAction Stop
                    } else {
                        Invoke-Expression -Command $makeExpression -ErrorAction Stop | Out-Null
                    }

                    if ($lastexitcode -ne 0) {
                        exit $lastexitcode
                    }
                } catch {
                    Write-Host "Error: {0}" -f $error[0]

                    exit $lastexitcode
                }

                # build solution

                $numProcessors = $env:NUMBER_OF_PROCESSORS -as [int]
				$numProcessors *= 2
				
                if (!$numProcessors) {
                    $numProcessors = 2
                }
				
                $msbuild = "{0}\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe" -f $env:WINDIR
                $buildExpression = '{0} "LLVM.sln" /m:{1} /p:Platform={2} /p:Configuration={3}' -f $msbuild, $numProcessors, $platforms[$i], $targets[$j]

                try {
                    Write-Host ""
                    Write-Host "Building Solution"
                    Write-Host ""

                    if ($logLevel -ne "quiet") {
                        $buildExpression += " /verbosity:{0}" -f $logLevel

                        Invoke-Expression -Command $buildExpression -ErrorAction Stop
                    } else {
                        Invoke-Expression -Command $buildExpression -ErrorAction Stop | Out-Null
                    }

                    if ($lastexitcode -ne 0) {
                        exit $lastexitcode
                    }
                } catch {
                    Write-Host "Error: {0}" -f $error[0]

                    exit $lastexitcode
                }

                # generate source and destination paths

                $binPath = Resolve-Path ("lib\{0}\" -f $targets[$j])
                $includePath1 = Resolve-Path "..\..\include\llvm"
                $includePath2 = Resolve-Path "include\llvm"

                Set-Location $toolPath

                $versionPath = "..\Dependencies\{0}\" -f $versions[$k]
                $platformPath = "{0}{1}\" -f $versionPath, $platforms[$i]
                $targetPath = "{0}{1}\" -f $platformPath, $targets[$j]

                if (!(Test-Path $versionPath)) {
                    New-Item -ItemType directory -Path $versionPath | Out-Null
                }

                if (!(Test-Path $platformPath)) {
                    New-Item -ItemType directory -Path $platformPath | Out-Null
                }

                if (!(Test-Path $targetPath)) {
                    New-Item -ItemType directory -Path $targetPath | Out-Null
                }

                $versionPath = Resolve-Path $versionPath
                $platformPath = Resolve-Path $platformPath
                $targetPath = Resolve-Path $targetPath

                $binDestination = Join-Path $targetPath "\llvm\"
                $includeDestination = Join-Path $platformPath "\llvm\"

                if (!(Test-Path $includeDestination)) {
                    New-Item -ItemType directory -Path $includeDestination | Out-Null
                }

                if (!(Test-Path $binDestination)) {
                    New-Item -ItemType directory -Path $binDestination | Out-Null
                }

                # get lists of all possible files to copy

                $includeList1 = Get-ChildItem -Path $includePath1 -Include @("*.*") -Recurse -Force
                $includeList2 = Get-ChildItem -Path $includePath2 -Include @("*.*") -Recurse -Force
                $binList = Get-ChildItem -Path $binPath -Include @("llvm*.lib") -Recurse -Force

                if ($targets[$j] -eq "debug") {
                    $binList += Get-ChildItem -Path $workingPath -Include @("llvm*.pdb") -Recurse -Force
                }

                # filter all list to remove any unwanted stuff

                $includeList1 = $includeList1 | Where-Object { -not $_.psIsContainer }
                $includeList2 = $includeList2 | Where-Object { -not $_.psIsContainer }
                $binList = $binList | Where-Object { -not $_.psIsContainer }

                foreach ($ignoreName in $ignoreNames) {
                    $binList = $binList | Where-Object { $_.name -NotLike $ignoreName }
                }

                foreach ($ignoreType in $ignoreTypes) {
                    $includeList1 = $includeList1 | Where-Object { $_.extension -ne $ignoreType }
                    $includeList2 = $includeList2 | Where-Object { $_.extension -ne $ignoreType }
                }

                # copy all filtered list of files to there destination

                $count = 0

                Write-Host ""
                Write-Host "Copying Include Files"

                Set-Location $includePath1

                foreach ($file in $includeList1) {
                    $relativePath = Get-Item $file | Resolve-Path -Relative
                    $destFile = Join-Path $includeDestination $relativePath
                    $outputDirectory = Split-Path $destFile -Parent

                    if (!(Test-Path $destFile)) {
                        if ($logLevel -ne "quiet") {
                            Write-Host $file "->" $destFile
                        }

                        if (!(Test-Path $outputDirectory)) {
                            New-Item -ItemType directory -Path $outputDirectory | Out-Null
                        }

                        Copy-Item $file -Force -Destination $destFile | Out-Null

                        $count++
                    }
                }

                Set-Location $includePath2

                foreach ($file in $includeList2) {
                    $relativePath = Get-Item $file | Resolve-Path -Relative
                    $destFile = Join-Path $includeDestination $relativePath
                    $outputDirectory = Split-Path $destFile -Parent

                    if (!(Test-Path $destFile)) {
                        if ($logLevel -ne "quiet") {
                            Write-Host $file "->" $destFile
                        }

                        if (!(Test-Path $outputDirectory)) {
                            New-Item -ItemType directory -Path $outputDirectory | Out-Null
                        }

                        Copy-Item $file -Force -Destination $destFile | Out-Null

                        $count++
                    }
                }

                Write-Host ""
                Write-Host "Copying Binary Files"

                foreach ($file in $binList) {
                    $destFile = Join-Path $binDestination $file.Name

                    if (!(Test-Path $destFile)) {
                        if ($logLevel -ne "quiet") {
                            Write-Host $file "->" $destFile
                        }

                        Copy-Item $file -Force -Destination $destFile | Out-Null

                        $count++
                    }
                }

                Write-Host ""
                Write-Host $count "file(s) copied"
            }
        }
    }
}

Write-Host ""
Write-Host "Complete"
Write-Host ""

Set-Location $toolPath